/*
 *  Copyright (C) 2010 Matthias Buch-Kromann <mbk.isv@cbs.dk>
 * 
 *  This file is part of the MatrixParser package.
 *  
 *  The MatrixParser program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.osdtsystem.matrixparser.featureextraction;

import java.util.BitSet;
import org.osdtsystem.matrixparser.features.FeatureHandler;
import org.osdtsystem.matrixparser.features.FeatureVector;
import org.osdtsystem.matrixparser.features.keys.StringListFactory;
import org.osdtsystem.matrixparser.parsers.ParsingModel;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public abstract class AbstractFeatureExtractor implements FeatureExtractor {
    final FeatureHandler featureHandler;
    private int geneFirst = -1;
    private int geneLast = -1;
    private final int geneCount;
    final static boolean recordMulti = true;
    final static boolean recordUsage = true;

    public AbstractFeatureExtractor(ParsingModel model, String geneFamily, int geneCount) {
        featureHandler = model.featureHandler();
        geneFirst = geneLast = newGenes(geneFamily, geneCount);
        this.geneCount = geneCount;
    }

    public AbstractFeatureExtractor(ParsingModel model) {
        this(model, "", 0);
    }

    public final FeatureHandler featureHandler() {
        return featureHandler;
    }

    // Gene handling
    public final int newGenes(String name, int count) {
        int firstGene = geneNames.size();
        for (int i = 0; i < count; ++i)
            geneNames.add(name + ":" + i);
        return firstGene;
    }

    void setLastGene(int lastGene) {
        geneLast = Math.max(geneLast, lastGene);
        if (geneLast - geneFirst > geneCount)
            throw new IllegalStateException("Actual gene count exceeds allocated gene count: "
                    + geneUse());
    }

    int firstGene() {
        return geneFirst;
    }
    
    public final boolean on(int gene) {
        if (recordUsage)
            usedGenes.set(gene);
        return ! deadGenes.get(gene);
    }

    public final boolean onMulti(int gene) {
        if (recordMulti)
            multiGenes.set(gene);
        return on(gene);
    }

    public String geneUse() {
        return getClass().getSimpleName()
                + " gene use: start=" + geneFirst + " end=" + geneLast;
    }

    public String geneUseActual() {
        return bitsetAsCompactString(usedGenes);
    }

    public String genesMulti() {
        return bitsetAsCompactString(multiGenes);
    }

    public String bitsetAsCompactString(BitSet bitset) {
        return bitsetAsCompactString(bitset, false);
    }

    public String bitsetAsCompactString(BitSet bitset, boolean invert) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        String sep = "";
        for (int i = 0; i < bitset.size(); ++i) {
            if (bitset.get(i) ^ invert) {
                sb.append(sep);
                int last = i;
                sb.append(i);
                while (bitset.get(++i)) {
                }
                if (i != last + 1) {
                    sb.append("-").append(i-1);
                }
                sep = ", ";
            }
        }
        sb.append("}");
        return sb.toString();
    }

    public static Object invertBitString(BitSet deadGenes) {
        BitSet inverted = new BitSet(deadGenes.size());
        inverted.set(0, deadGenes.size(), true);
        inverted.andNot(usedGenes);
        return inverted;
    }

    // Adding features
    final void addw(FeatureVector fv, String group, String f1, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1)), w);
    }

    final void addw(FeatureVector fv, String group, String f1, String f2, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2)), w);
    }

    final void addw(FeatureVector fv, String group, String f1, String f2, String f3, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3)), w);
    }

    final void addw(FeatureVector fv, String group, String f1, String f2, String f3, String f4, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4)), w);
    }

    final void addw(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5)), w);
    }

    final void addw(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, String f6, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5, f6)), w);
    }

    void addw(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, String f6, String f7, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5, f6, f7)), w);
    }

    void addw(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, String f6, String f7, String f8, double w) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5, f6, f7, f8)), w);
    }

    final void add(FeatureVector fv, String group, String f1) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2, String f3) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2, String f3, String f4) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, String f6) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5, f6)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, String f6, String f7) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5, f6, f7)), 1);
    }

    final void add(FeatureVector fv, String group, String f1, String f2, String f3, String f4, String f5, String f6, String f7, String f8) {
        fv.addValue(featureHandler.getFeature(StringListFactory.combine(group, f1, f2, f3, f4, f5, f6, f7, f8)), 1);
    }
}
